<?php

namespace app\common\service;

use think\exception\ValidateException;

class UploadService extends ApiService
{

    private $uploadType;

    private CosService|OssService $client;

    private $fileAllowExtension = ['docx', 'doc', 'pdf', 'pptx', 'ppt', 'xlsx', 'xls', 'txt'];
    private $imageAllowExtension = ['jpg', 'jpeg', 'png'];
    private $videoAllowExtension = ['mp4', 'mov'];
    private $audioAllowExtension = ['mp3'];

    private const TYPE_FILE = 'file';
    private const TYPE_IMAGE = 'image';
    private const TYPE_VIDEO = 'video';
    private const TYPE_AUDIO = 'audio';

    private $savePath = '';

    public function __construct()
    {
        $uploadType = env('upload_type');
        if (!$uploadType) {
            getResponseException('未配置上传方式', [], 1, 'ERROR');
        }
        if (!in_array($uploadType, ['cos', 'oss'])) {
            getResponseException('上传方式不符合要求', [], 1, 'ERROR');
        }
        $this->uploadType = $uploadType;
        $this->getClient();
        $this->savePath = 'uploads/' . date('Ymd');
    }

    public function getDomain()
    {
        return $this->client->getDomain();
    }


    public function getSts()
    {
        $sts = $this->client->getSts();
        if (!$sts) {
            $this->setError($this->client->getError());
            return false;
        }
        $name = $this->generateUniqueFileName();
        return [
            'type' => $this->client->getType(),
            'ext' => [
                "*"
            ],
            'path' => $this->savePath . "/{$name}",
            'size' => [
                'min' => -1,
                'max' => -1
            ],
            'sts' => $sts,
            'info' => $this->client->getInfo()
        ];
    }

    public function copyObject($filePath, $savePath)
    {
        $downloadBytes = file_get_contents($filePath);
        return $this->uploadFileSimple($downloadBytes, $savePath);
    }

    /**
     * 获取cos客户端
     */
    private function getClient(): void
    {
        switch ($this->uploadType) {
            case 'cos':
                $this->client = new CosService();
                break;
            case 'oss':
                $this->client = new OssService();
                break;
        }
    }

    /**
     * 下载图片
     * @param string $sourcePath 下载文件路径
     * @return mixed
     */
    public function downloadBody(string $sourcePath): mixed
    {
        // 配置参数
        // 1. 从 COS/OSS 下载原图
        $result = $this->client->getObject($sourcePath);
        if (!$result) {
            $this->setError($this->client->getError());
            return false;
        }
        return $result;

    }

    /**
     * 上传文件
     * @param string $object 文件流
     * @param string $fileName 上传文件的文件名
     * @param string $savePath 保存路径
     * @return bool|string
     */
    public function uploadFile($object, string $fileName, string $savePath = ''): bool|string
    {
        try {
            // 获取文件扩展名
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);
            $fileExtension = $this->getAllowExtension(self::TYPE_FILE);
            $fileExtensionText = implode(',', $fileExtension);
            if (!in_array(strtolower($extension), $fileExtension)) {
                throw new ValidateException("格式{$extension}不被允许，该方法仅支持{$fileExtensionText}格式");
            }
            // 生成路径
            if (!$savePath) {
                $savePath = $this->savePath;
            }

            $savePath = trim($savePath, '/') . '/' . $this->generateUniqueFileName($extension);

            $object = file_get_contents($object);
            // 上传
            $result = $this->client->putObject($savePath, $object);
            if (!$result) {
                throw new ValidateException($this->client->getError());
            }
        } catch (\Exception|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }

    /**
     * 上传文件(简化版)
     * @param string $object 文件流
     * @param string $savePath 存储路径
     * @return bool|string
     */
    public function uploadFileSimple(string $object, string $savePath = ''): bool|string
    {
        try {
            // 上传
            $result = $this->client->putObject($savePath, $object);
            if (!$result) {
                throw new ValidateException('上传失败');
            }
        } catch (\Exception|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }

    /**
     * 上传视频
     * @param string $object 文件流
     * @param string $fileName 上传文件的文件名
     * @param string $savePath 保存路径
     * @return bool|string
     */
    public function uploadVideo(string $object, string $fileName, string $savePath = ''): bool|string
    {
        try {
            // 获取文件扩展名
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);
            // 限制只能上传视频格式
            $fileExtension = $this->getAllowExtension(self::TYPE_VIDEO);
            $fileExtensionText = implode(',', $fileExtension);
            if (!in_array(strtolower($extension), $fileExtension)) {
                throw new ValidateException("格式{$extension}不被允许，该方法仅支持{$fileExtensionText}格式");
            }
            // 生成路径
            if (!$savePath) {
                $savePath = 'videos/' . date('Ymd');
            }
            $savePath = trim($savePath, '/') . '/' . $this->generateUniqueFileName($extension);

            $object = file_get_contents($object);
            // 上传
            $result = $this->client->putObject($savePath, $object);
            if (!$result) {
                throw new ValidateException('上传失败');
            }
        } catch (\Exception|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }


    /**
     * 上传图片
     * @param string $object 文件流
     * @param string $fileName 上传文件的文件名
     * @param string $savePath 保存路径
     * @return bool|string
     */
    public function uploadImg(string $object, string $fileName, string $savePath = ''): bool|string
    {
        try {
            // 获取文件扩展名
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);
            // 限制只能上传图片格式
            $fileExtension = $this->getAllowExtension(self::TYPE_IMAGE);
            $fileExtensionText = implode(',', $fileExtension);
            if (!in_array(strtolower($extension), $fileExtension)) {
                throw new ValidateException("格式{$extension}不被允许，该方法仅支持{$fileExtensionText}格式");
            }
            // 生成路径
            if (!$savePath) {
                $savePath = 'image/' . date('Ymd');
            }
            $savePath = trim($savePath, '/') . '/' . $this->generateUniqueFileName($extension);

            $object = file_get_contents($object);

            // 上传到COS
            $result = $this->client->putObject($savePath, $object);
            if (!$result) {
                throw new ValidateException('上传失败');
            }
        } catch (\Exception|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }


    /**
     * 上传音频
     * @param string $object 文件流
     * @param string $fileName 上传文件的文件名
     * @param string $savePath 保存路径
     * @return bool|string
     */
    public function uploadAudio(string $object, string $fileName, string $savePath = ''): bool|string
    {
        try {
            // 获取文件扩展名
            $extension = pathinfo($fileName, PATHINFO_EXTENSION);
            // 限制只能上传图片格式
            $fileExtension = $this->getAllowExtension(self::TYPE_AUDIO);
            $fileExtensionText = implode(',', $fileExtension);
            if (!in_array(strtolower($extension), $fileExtension)) {
                throw new ValidateException("格式{$extension}不被允许，该方法仅支持{$fileExtensionText}格式");
            }
            // 生成路径
            if (!$savePath) {
                $savePath = 'audio/' . date('Ymd');
            }
            $savePath = trim($savePath, '/') . '/' . $this->generateUniqueFileName($extension);

            $object = file_get_contents($object);

            // 上传到COS
            $result = $this->client->putObject($savePath, $object);
            if (!$result) {
                throw new ValidateException('上传失败');
            }
        } catch (\Exception|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $result;
    }


    /**
     * @param $type
     * @return array
     */
    private function getAllowExtension($type): array
    {
        $fileAllowExtension = $this->fileAllowExtension;
        $imageAllowExtension = $this->imageAllowExtension;
        $videoAllowExtension = $this->videoAllowExtension;
        $audioAllowExtension = $this->audioAllowExtension;
        switch ($type) {
            case self::TYPE_FILE:
                $allowExtension = array_merge($fileAllowExtension, $imageAllowExtension, $videoAllowExtension,$audioAllowExtension);
                break;
            case self::TYPE_IMAGE:
                $allowExtension = $imageAllowExtension;
                break;
            case self::TYPE_VIDEO:
                $allowExtension = $videoAllowExtension;
                break;
            case self::TYPE_AUDIO:
                $allowExtension = $audioAllowExtension;
                break;
            default:
                $allowExtension = [];
                break;
        }
        return $allowExtension;
    }


    /**
     * 生成唯一文件名
     * @param $extension string 扩展名
     * @param $prefix string 前缀
     * @param $separator string 分隔符
     * @return string
     */
    public function generateUniqueFileName(string $extension = '', string $prefix = '', string $separator = '_'): string
    {
        // 前缀处理
        $prefix = $prefix ? $prefix . $separator : '';

        // 生成唯一标识（时间戳+随机字符串）
        $timestamp = date('YmdHis');
        $randomStr = substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 6);

        // 扩展名处理
        $extension = $extension ? '.' . $extension : '';

        // 组合文件名
        return $prefix . $timestamp . $separator . $randomStr . $extension;
    }
}
